package aconcagua.measure

import static aconcagua.Aconcagua.INFINITY;
import static aconcagua.Aconcagua.MINUS_INFINITY;

class Infinity extends MeasureBehavior {

	// accessing
	@Override
	public amount() {
		return this;
	}

	public baseUnit() {
		return new NullUnit();
	}
	
	@Override
	public denominator() {
		return 1;
	}
	
	@Override
	public measures() {
		return this.isNothing() ? [] : [this];
	}
	
	@Override
	public numerator() {
		return this;
	}
	
	@Override
	public unit() {
		return this.baseUnit();
	}

	@Override
	public unitNameOf(aUnit) {
		return aUnit.nameForMany();
	}
	
	// arithmetic operations	
	@Override
	public div(aMeasure) {
		this.throwIndeterminateOperationException('Division', this, aMeasure);
	}
	
	public divideFrom(aNumber) {
		return 0;
	}

	@Override
	public intdiv(aMeasure) {
		this.throwIndeterminateOperationException('Integer Division', this, aMeasure);
	}

	@Override
	public mod(aMeasure) {
		this.throwIndeterminateOperationException('Module', this, aMeasure);
	}

	@Override
	public multiply(aMeasure) {
		if (aMeasure.isNothing())
			this.throwIndeterminateOperationException('Multiplication', this, aMeasure);
		return aMeasure * this;
	}

	@Override
	public plus(aMeasure) {
		return this;
	}
	
	@Override
	public minus(aMeasure) {
		return this;
	}
	
	@Override
	public minus(Infinity anInfinity) {
		this.throwIndeterminateOperationException('Subtract', anInfinity, this);
	}
	
	@Override
	public multiply(Infinity anInfinity) {
		return this;
	}
	
	@Override
	public multiply(Number aNumber) {
		if (aNumber.isNothing())
			this.throwIndeterminateOperationException('Multiplication', this, aNumber);
		return (aNumber > 0)? this : this.negate()
	}
	
	@Override
	public plus(MinusInfinity anInfinity) {
		this.throwIndeterminateOperationException('Sum', this, anInfinity);
	}

	@Override
	public negate() {
		return MINUS_INFINITY;
	}
	
	@Override
	public round(aNumber) {
		return this;
	}

	@Override
	public trunc() {
		return this;
	}

	// arithmetic operations - private
	private addMeasure(aMeasure) {
		return this;
	}
	
	// comparing
	@Override
	public boolean equalsMeasure(aMeasure) {
		return this == aMeasure;
	}
	
	@Override
	public boolean equalsMeasureBag(aMeasureBag) {
		return this == aMeasureBag;
	}
	
	@Override
	public int compareTo(aMeasure) {
		return aMeasure == INFINITY ? 0 : 1;
	}
	
	// converting
	public convertTo(aTargetUnit) {
		if (!this.unit().sameDomainAs(aTargetUnit))
			throw new CanNotConvertMeasureException(this, aTargetUnit)
		else return this.convertInSameDomainTo(aTargetUnit);
	}
	
	public convertToBaseUnit() {
		return this.convertTo(this.baseUnit());
	}
	
	// converting - private
	private convertInSameDomainTo(aTargetUnit) {
		return aTargetUnit.convertFromBaseUnit(this.unit().convertToBaseUnit(this));
	}

	// interval protocol
	public to(aStop) {
	 	return this.to(aStop, this.unit().with(1));
	}
	
	public to(aStop, aStep) {
		return new ArithmeticObjectInterval(this,
			aStop,
			aStep,
			new InfinityIntervalStrategy());
	}
	
	// testing
	@Override
	public boolean isMeasureBag() {
		return false;
	}
	
	@Override
	public boolean isNothing() {
		return false;
	}
	
	// exceptions throwing
	private void throwIndeterminateOperationException(anOperation, aLeftOperand, aRightOperand) {
		throw new IndeterminateOperationException(anOperation,
			aLeftOperand,
			aRightOperand);
	}
	
	// printing
	@Override
	public String toString() {
		return "Infinity";
	}
}
